home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-01-23 | 12.3 KB | 417 lines | [TEXT/CCL2] |
- ;;; -*- Mode: LISP; Syntax: Common-lisp; Package: COMMON-LISP-USER; Base: 10 -*-
- ;;; MT's utility library
- ;;; Copyright Mike Travers 1987 et(c)etera.
- ;;; not yet completely converted to MCL
-
- ;;; Some things here stolen from others.
-
- ;;; Small syntactical aids.
-
- (in-package :cl-user)
-
- (defmacro non-nil (var)
- `(and (boundp ',var)
- ,var))
-
- (declaim (ignore ignore)) ; So sue me
-
- ;;; Lifted from PCL. Ensure that a macro variable is only expanded once.
- #-GENERA
- (defmacro once-only (vars &body body)
- (let ((gensym-var (gensym))
- (run-time-vars (gensym))
- (run-time-vals (gensym))
- (expand-time-val-forms ()))
- (dolist (var vars)
- (push `(if (or (symbolp ,var)
- (numberp ,var)
- (and (listp ,var)
- (member (car ,var) '(quote function))))
- ,var
- (let ((,gensym-var (gensym)))
- (push ,gensym-var ,run-time-vars)
- (push ,var ,run-time-vals)
- ,gensym-var))
- expand-time-val-forms))
- `(let* (,run-time-vars
- ,run-time-vals
- (wrapped-body
- ((lambda ,vars . ,body) . ,(reverse expand-time-val-forms))))
- `((lambda ,(nreverse ,run-time-vars) ,wrapped-body)
- . ,(nreverse ,run-time-vals)))))
-
- (defmacro return-if (val)
- (once-only (val)
- `(if ,val (return ,val))))
-
- (defmacro return-from-if (block val)
- (once-only (val)
- `(if ,val (return-from ,block ,val))))
-
- #-CCL
- (defmacro neq (a b)
- `(not (eq ,a ,b)))
-
- ;;; Iteration and Mapping
-
- (defmacro dosequence ((var sequence &optional result) &body body)
- `(dolist (,var (coerce ,sequence 'list) ,result) ,@body))
-
- ;;; Sequence functions (but only working on lists for now)
- ;;; +++ flush return-max, return multiple-values
- (defun extreme (list test &key (key #'identity) (return-max nil))
- (and list
- (let* ((best (car list))
- (max (funcall key best)))
- (dolist (other (cdr list) (if return-max max best))
- (let ((score (funcall key other)))
- (when (funcall test score max)
- (setq best other max score)))))))
-
- (defun extremes (list test &key (key #'identity))
- (if list
- (let* ((best (list (car list)))
- (max (funcall key (car best))))
- (dolist (other (cdr list) (values best max))
- (let ((score (funcall key other)))
- (if (funcall test score max)
- (setq best (list other) max score)
- (if (funcall test max score)
- nil
- (push other best))))))
- (values nil most-negative-fixnum)))
-
- (defun maximize (list &key (key #'identity) (return-max nil))
- (declare (inline extreme)) ; not that this does anything
- (extreme list #'> :key key :return-max return-max))
-
- (defun minimize (list &key (key #'identity) (return-max nil))
- (declare (inline extreme)) ; not that this does anything
- (extreme list #'< :key key :return-max return-max))
-
- (defun maximums (list &key (key #'identity))
- (declare (inline extremes)) ; not that this does anything
- (extremes list #'> :key key))
-
- (defun minimums (list &key (key #'identity))
- (declare (inline extremes)) ; not that this does anything
- (extremes list #'< :key key))
-
- (defun random-element (list)
- (and list
- (nth (random (length list)) list)))
-
- (defmacro do-for-array-elements (array vars &body body)
- `(let ((array-dimensions (array-dimensions ,array)))
- (do-for-array-elements-1 ,array ,vars 0 ,@body)))
-
- (defmacro do-for-array-elements-1 (array vars dim &body body)
- (if vars
- `(dotimes (,(car vars) (nth ,dim array-dimensions))
- (do-for-array-elements-1 ,array ,(cdr vars) ,(1+ dim)
- ,@body))
- `(progn ,@body)))
- ;;; do-collect
- ;;; generalized good iterator.
-
- (defun circular-list (&rest elements)
- (rplacd (last elements) elements))
-
- (defun string-replace-char (string char0 char1 &key (start 0) (end nil))
- (do ((from start)
- (new-string (concatenate 'string string)))
- ((null from) new-string)
- (setq from (position char0 string :start (1+ from) :end end))
- (when from
- (setf (char new-string from) char1))))
-
- ;;; These stolen from KWH
- (defun collect (fcn list)
- "Applies FCN to each element of LIST returning all the non-nil values as a list."
- (let* ((head (list 'HEAD))
- (tail head))
- (dolist (elt list (cdr head))
- (let ((value (funcall fcn elt)))
- (when value
- (push value (cdr tail))
- (setf tail (cdr tail)))))))
-
- (defun mapappend (fcn list)
- "Applies FCN to every element of LIST, appending the results together.
- Order is maintained as one might expect."
- (let* ((head (list '())) (tail head))
- (dolist (elt list (cdr head))
- (dolist (result-elt (funcall fcn elt))
- (setf (cdr tail) (list result-elt))
- (setf tail (cdr tail))))))
-
- (defun mapunion (fcn list)
- "Applies FCN to every element of LIST, unioning the results together.
- Except for removal of EQL occurences, order is maintained as one might expect."
- (let* ((head (list '())) (tail head))
- (dolist (elt list (cdr head))
- (dolist (result-elt (funcall fcn elt))
- (unless (member result-elt head)
- (setf (cdr tail) (list result-elt))
- (setf tail (cdr tail)))))))
-
- (defun mapcross (fcn list1 list2)
- "Applies FCN to every combination of elements from LIST1 and LIST2,
- returning the list of results. Order is maintained as one might expect."
- (let* ((head (list '())) (tail head))
- (dolist (e1 list1 (cdr head))
- (dolist (e2 list2)
- (push (funcall fcn e1 e2) (cdr tail))
- (setf tail (cdr tail))))))
-
- (defun split-list (predicate list)
- "Returns two lists extracted from list based on PREDICATE."
- (let ((wheat '()) (chaff '()))
- (dolist (elt list (values wheat chaff))
- (if (funcall predicate elt)
- (push elt wheat) (push elt chaff)))))
-
- (defun filter (predicate list &aux wheat)
- "Return only the elements of list meeting PREDICATE"
- (dolist (elt list wheat)
- (when (funcall predicate elt)
- (push elt wheat))))
-
-
- ;;; String Parse Utility
-
- ;;; Generalized variables, binding, etc.
-
- (defmacro deletef (thing place &rest delete-args)
- (once-only (place)
- `(setf ,place (delete ,thing ,place ,@delete-args))))
-
- ;;; mv-let*: lets the car of a let binding form be a list
- ;;; elements of which get bound to multiple values.
-
- (defmacro mv-let* (forms &body body)
- (cond ((null forms)
- `(progn ,@body))
- ((or (symbolp (car forms))
- (symbolp (caar forms)))
-
- `(let (,(car forms))
- (mv-let* ,(cdr forms)
- ,@body)))
- (t
- `(multiple-value-bind ,(caar forms) ,(cadar forms)
- (mv-let* ,(cdr forms)
- ,@body)))))
-
- #+GENERA
- (defmacro bind-keyword-vars ((arglist keyvars) &body body)
- `(let ,keyvars
- (zl:keyword-extract ,arglist ,(gensym) ,keyvars nil
- ,@body)))
-
- ;;; destructuring-let
-
- ;;; Function hacking
-
- ; Define a function that caches its values. The function should be a function
- ; in the mathematical sense (a mapping with no state). It can't take a rest or
- ; optional args.
- (defmacro def-cached-function (name arglist &body body)
- (let ((ht (make-hash-table :test #'equal)))
- `(defun ,name (&rest args)
- (multiple-value-bind (val found)
- (gethash args ,ht)
- (if found
- val
- (setf (gethash (copy-list args) ,ht)
- (destructuring-bind ,arglist args
- ,@body)))))))
-
- (defmacro test-defun (name args &body body)
- `(progn
- (defun ,name ,args ,@body)
- (compile ',name)
- (disassemble ',name)))
-
- #+GENERA
- (defun allow-redefinition (fspec &optional (type 'defun))
- (when (boundp 'si:fdefine-file-definitions)
- (si:allow-redefinition fspec type)))
-
- (defmacro defsubst (name args &body body)
- `(progn
- (defun ,name ,args
- ,@body)
- (declaim (inline ,name))))
-
- #+MCL (pushnew "subst"
- (cdr (assoc 'function ccl::*define-type-alist*)))
-
-
- ;;; Numerics
-
- (defmacro ^ (x y)
- `(expt ,x ,y))
-
- (defsubst sign (num)
- (cond ((plusp num) 1)
- ((minusp num) -1)
- (t 0)))
-
- (defsubst abs-max (max num)
- (if (<= (abs num) max)
- num
- (* max (sign num))))
-
- #+GENERA
- (defmacro now () '(time:time))
- #+CCL
- (defmacro now () '(get-internal-real-time)) ; no idea if this is best, but it's something
-
- (defun arand (center range)
- (+ center (random (* 2.0 range)) (- range)))
-
- (eval-when (load compile eval)
- (defconstant single-pi (coerce pi 'single-float))) ;Avoid introducing double-floats
- (defconstant degrees-to-radians (/ (* 2 single-pi) 360))
- (defconstant radians-to-degrees (/ degrees-to-radians))
- (defmacro d2r (deg)
- `(* degrees-to-radians ,deg))
- (defmacro d2ri (deg)
- (* degrees-to-radians deg))
- (defmacro r2d (rad)
- `(* radians-to-degrees ,rad))
-
- ;;; Fast integer arithmetic. More or less stolen from Boxer
-
- (defmacro int (x) `(the fixnum ,x))
- (defmacro def-arith-op (int-name reg-name)
- `(defmacro ,int-name (&rest args)
- ; (declare (arglist ,(arglist reg-name)))
- `(the fixnum (,',reg-name ,@(mapcar #'(lambda (arg) `(the fixnum ,arg)) args)))))
-
- (def-arith-op +& +) ; Only addition-type ops actually get any boost in MCL2.0
- (def-arith-op -& -)
- (def-arith-op incf& incf)
- (def-arith-op decf& decf)
- (def-arith-op 1+& 1+)
- (def-arith-op 1-& 1-)
- #|
- (def-arith-op *& *)
- (def-arith-op /& /)
- (def-arith-op max& max)
- (def-arith-op min& min)
- |#
-
-
- ;;; Safety-last versions of these (note: in MCL2.0, svref& compiles inline, aref& does not)
- (defmacro svref& (vector index)
- `(let ()
- (declare (optimize (speed 3) (safety 0)))
- (svref ,vector (the fixnum ,index))))
-
- (defmacro aref& (array &rest indicies)
- `(let ()
- (declare (optimize (speed 3) (safety 0)))
- (aref ,array ,@(mapcar #'(lambda (index) `(the fixnum ,index)) indicies))))
-
- (defun integers (from to)
- (if (equal from to) (list from)
- (cons from (integers (+ from (sign (- to from))) to))))
-
- (defun log2 (x)
- (/ (log x) (log 2)))
-
- (defun number-of-bits (n) ;;; smallest k st 2**kn
- (ceiling (log2 n)))
-
- (defun average (list)
- (if (null list) 0
- (/ (apply #'+ list) (length list))))
-
- (defun std-dev (list &aux (average (average list)))
- (sqrt (/ (apply #'+
- (mapcar #'(lambda (x) (expt (- x average) 2))
- list))
- (length list))))
-
- (defun geo-mean (list)
- (nth-root (apply #'* list) (length list)))
-
- (defun nth-root (x n)
- (if (> x 0) (exp (/ (log x) n))
- (error "In NTH-ROOT, X=~S is not positive." x)))
-
- (defvar pi/2 (/ single-pi 2.0))
- (defvar pi/4 (/ single-pi 4.0))
- (defvar 2pi (* single-pi 2))
-
- (defun symbol-conc (&rest parts)
- (intern (apply #'concatenate 'string (mapcar 'string parts))))
-
- ;;; stolen from CLIM
- (defun format-time (time &optional (stream *standard-output*))
- (let (second minute hour date month year day daylight-savings-p time-zone)
- (multiple-value-setq
- (second minute hour date month year day daylight-savings-p time-zone)
- (get-decoded-time))
- (multiple-value-setq
- (second minute hour date month year day daylight-savings-p time-zone)
- (if (<= 5 time-zone 8) ;US-centric, to be sure
- (decode-universal-time time)
- (decode-universal-time time 0)))
- (princ (nth day
- '("Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun"))
- stream)
- (princ " " stream)
- (princ (nth (1- month)
- '("Jan" "Feb" "Mar" "Apr" "May" "June"
- "July" "Aug" "Sept" "Oct" "Nov" "Dec"))
- stream)
- (format stream " ~2D ~2D:~2,'0D:~2,'0D " date hour minute second)
- (cond ((= time-zone 0)
- (princ "GMT" stream))
- (t (princ (nth (- time-zone 5)
- '("E" "C" "M" "P"))
- stream)
- (princ (if daylight-savings-p "D" "S") stream)
- (princ "T")))
- (format stream " ~4D" year)))
-
- ;;; CL provides no externalp function, and neither does
- ;;; MCL (although it keeps this info with the symbol).
- (defun externalp (symbol)
- (multiple-value-bind (ignore type)
- (find-symbol (symbol-name symbol) (symbol-package symbol))
- (eq type :external)))
-
- ;;; Packages
-
- ; +++ make this a setf method
- (defun add-nickname (package nickname)
- (rename-package package
- (package-name package)
- (adjoin nickname (package-nicknames package) :test #'string-equal)))
-
- ;;; Streams
-
- (defun stream-copy (in out)
- (do (char) (())
- (setq char (read-char in nil :eof))
- (if (eq char :eof)
- (return)
- (write-char char out))))
-
- ;;; CLOS
-
- (defclass plist-mixin () ((plist :initform nil)))
-
- (defmethod oget ((o plist-mixin) property &optional (default nil))
- (getf (slot-value o 'plist) property default))
-
- (defmethod oput ((o plist-mixin) property value)
- (setf (getf (slot-value o 'plist) property)
- value))
-
- (provide :mt-utils)
-